Introduction


Download raw data from Uppmax


#rsync -r noahe@rackham.uppmax.uu.se:/proj/g2020004/private/student_projects/phosphoproteomics /home/noah/phosphoproteomics_project/

Setting working directory and install libraries

setwd("~/phosphoproteomics_project")
RequiredPackages <- c("BiocManager", "dplyr", "igraph", "tidyr","ggplot2","ggrepel") 
#Installs packages if not yet installed
for (i in RequiredPackages) { 
    if (!require(i, character.only = TRUE)) 
    install.packages(i)
}
BiocManager::install(version = "3.15")
library(BiocManager)
BiocManager::install("STRINGdb")
library(tidyr)
library(dplyr)
library(igraph)
library(STRINGdb)
library(ggplot2)
library(ggrepel)
remove(RequiredPackages)

Load raw data

raw_data_phospho <- read.csv(file = "/home/noah/phosphoproteomics_project/phosphoproteomics/Dataset_Phospho.csv", sep = ";")
raw_data_proteom <- read.csv(file = "/home/noah/phosphoproteomics_project/phosphoproteomics/Dataset_Proteome.csv", sep = ";")

If loop to omit na.value from all Abundance columns

Abundance_columns <- grep("Abundance", colnames(raw_data_phospho) )
for (i in Abundance_columns) {
  data_phospho <- raw_data_phospho[!(is.na(raw_data_phospho[,i])), ]
  }

Calculate Mean for Abundance and adding it as column and Ratio LIF/Control

Abundance_columns <- grep("Abundance.Control", colnames(data_phospho))
data_phospho$Abudance.Mean.Control <- rowMeans(data_phospho[,c(Abundance_columns)])

Abundance_columns <- grep("Abundance.LIF", colnames(data_phospho))
data_phospho$Abudance.Mean.LIF <- rowMeans(data_phospho[,c(Abundance_columns)])

data_phospho$Abudance.Ratio <- data_phospho$Abudance.Mean.LIF / data_phospho$Abudance.Mean.Control

remove(Abundance_columns, i)

Calculate p-values

start_for <- Sys.time()
for (row in 1:nrow(data_phospho)) {
  p_value <- t.test(data_phospho[row,10:12], data_phospho[row,13:15], alternative = "less") 
  #tests if mean of control is different from mean of LIF (case) and assigns it to list p_value
  data_phospho$Abudance.pvalue[row] <- p_value$p.value 
  #selects p.value from list and assigns it to new column"Abudance.pvalue" in the correct row
  }
end_for <- Sys.time()
end_for - start_for #We wanted to compare calculation time to compare the methods
Time difference of 1.649213 secs
#Alice quicker approach
df_copy <- dplyr::tibble(data_phospho) # Copy df
start_alice <- Sys.time()
df_copy[] <- lapply(df_copy, function(x) as.numeric(as.character(x)))
df_copy$pValues <- apply(df_copy, 1, function(x) t.test(x[10:12],x[13:15], alternative = "less", paired = F)$p.value)
end_alice <- Sys.time()
end_alice-start_alice #Proves that apply is x4 faster then loop
Time difference of 0.5273037 secs
remove(end_alice,end_for,start_alice,start_for,row,p_value, df_copy)

Adding a Volcano plot to explore the initial data

### Phospho volcano plot
string_db_volcano <- STRINGdb$new( version="11.5", species=10090, score_threshold=1, input_directory="")
# This had to be outside the volcano_phospho_plot function, because the function didnt return the string_db_volcano function that is needed also afterwards.

# Defining function to plot data
    # Makes the whole code easy to reuse.

volcano_phospho_plot <- function(phospho, number){

# Calculate LIF/Control ratio
phospho$FC <- phospho$Abudance.Mean.LIF / phospho$Abudance.Mean.Control
phospho$log2_FC <- log2(phospho$FC)
phospho$diffexpressed <- "NO"
phospho$diffexpressed[phospho$log2_FC > 1 & phospho$Abudance.pvalue < 0.05] <- "UP" # sorts out upregulated phospo values and assigns "UP" value.
phospho$diffexpressed[phospho$log2_FC < -1 & phospho$Abudance.pvalue < 0.05] <- "DOWN" # same or downregulated phospho values

phospho$delabel <- NA
phospho$delabel[phospho$diffexpressed != "NO"] <- phospho$Master.Protein.Accessions[phospho$diffexpressed != "NO"] # selecting only up or down regulated values and assigning them to the label column.

##Addition to generate "real names" not Uniprot IDs
Mapping_phospho <- string_db_volcano$map(phospho, "delabel", removeUnmappedRows = F)
Mapping_phospho <- string_db_volcano$add_proteins_description(Mapping_phospho) #Add´s protein information to STRINGid´s = preferred names are more human readable

#Volcano plot overview
ggplot(data=Mapping_phospho, aes(x=log2_FC, y=-log10(Abudance.pvalue), col=diffexpressed, label=preferred_name))+
  geom_point()+
  theme_minimal()+
  geom_vline(xintercept=c(-1, 1), col="red") +
  geom_hline(yintercept=-log10(0.05), col="red")
#saves the correct photo for both possible input tables
# number 1 or 2 has to be defined in the function input data. This is a bit clumsy solved, but there were problems with returning more than one plot out of a function.
if (number == 1) {
  ggsave("~/phosphoproteomics_project/results/Zoomed_out_Volcano.png", width = 1500, height = 1000, units = "px") # Saves plots with defined size
}
if (number == 2) {
  ggsave("~/phosphoproteomics_project/results/Zoomed_out_Volcano_for_more_phosphrylated_after_qc_and_filter.png", width = 1500, height = 1000, units = "px")
}
#Detail/Zoomed in
ggplot(data=Mapping_phospho, aes(x=log2_FC, y=-log10(Abudance.pvalue), col=diffexpressed, label=preferred_name))+
  geom_point()+
  theme_minimal()+
  geom_vline(xintercept=c(-1, 1), col="red") +
  geom_hline(yintercept=-log10(0.05), col="red") + 
  coord_cartesian(xlim = c(1,3.5), ylim = c(0.5, 6))+
  geom_label_repel()

#saves the correct photo for both possible input tables

if (number == 1) {
  ggsave("~/phosphoproteomics_project/results/Zoomed_in_Volcano.png", width = 1500, height = 1000, units = "px")
}
if (number == 2) {
ggsave("~/phosphoproteomics_project/results/Zoomed_in_Volcano_for_more_phosphrylated_after_qc_and_filter.png", width = 1500, height = 1000, units = "px")
}
}

volcano_phospho_plot(data_phospho, 1) #plots data to control
trying URL 'https://stringdb-static.org/download/protein.aliases.v11.5/10090.protein.aliases.v11.5.txt.gz'
Content type 'application/octet-stream' length 13598694 bytes (13.0 MB)
==================================================
downloaded 13.0 MB

trying URL 'https://stringdb-static.org/download/protein.info.v11.5/10090.protein.info.v11.5.txt.gz'
Content type 'application/octet-stream' length 1839554 bytes (1.8 MB)
==================================================
downloaded 1.8 MB
Warning:  we couldn't map to STRING 98% of your identifiers

Overwiev Volcano Phosho

Zoomed in Volcano plot Phospho


Proteome volcano plot

# Defining function first
volcano_plot_proteome <- function(proteome){
proteome$log2_FC <- log2(proteome$Abundance.Ratio...LIF.....Control.)
proteome$diffexpressed <- "NO"
proteome$diffexpressed[proteome$log2_FC > 1 & proteome$Abundance.Ratio.P.Value...LIF.....Control. < 0.05] <- "UP"
proteome$diffexpressed[proteome$log2_FC < -1 & proteome$Abundance.Ratio.P.Value...LIF.....Control. < 0.05] <- "DOWN"

ggplot(data=proteome, aes(x=log2_FC, y=-log10(Abundance.Ratio.P.Value...LIF.....Control.), col=diffexpressed))+
  geom_point()+
  theme_minimal()+
  geom_vline(xintercept=c(-1, 1), col="red") +
  geom_hline(yintercept=-log10(0.05), col="red")
}
#Printing the volcano plot
volcano_plot_proteome(raw_data_proteom) # Plots all the data 
Warning: Removed 852 rows containing missing values (geom_point).


Subsetting data_phospho for ratio values >= 2 and p-values <= 0.05

##Filter NA out in proteome, Filter out p.values that are non significant(not adjusted)
  #securing no overwrite to original data
df_phospho = data_phospho
df_proteome = raw_data_proteom
  
## Filter PHOSPHO
  more_phospho <- subset.data.frame(df_phospho, subset = df_phospho$Abudance.Ratio >= 2) #Filters out all ratios below 2-fold
  more_phospho <- subset.data.frame(more_phospho, subset = more_phospho$Abudance.pvalue <= 0.05) #Filters out all non-significant p-values
  
  ##Filter PROTEOME
  exclude_proteome <- filter(df_proteome, df_proteome$Abundance.Ratio.P.Value...LIF.....Control. <= 0.05 & df_proteome$Abundance.Ratio...LIF.....Control. >= 1) #Filters out all significant p-values, because significant p-values indicate an 
  #Data that we want to exclude, testing if the right data is selected with the volcano plot
  volcano_plot_proteome(exclude_proteome)

  #Anti_join anti_join() return all rows from x without a match in y. 
  more_proteome <- anti_join(df_proteome, exclude_proteome, by = c("Accession" = "Accession"))
  more_proteome <- filter(more_proteome, more_proteome$Abundance.Ratio...LIF.....Control. <= 2) # removes outliers with ratio bigger then 2
  #Checking the if the right data is excluded
  volcano_plot_proteome(more_proteome)
  
  #Inner join works too, but semi join is better becasue the proteome columns are not included into the resulting data frame.
  #more_phosphorylated <- dplyr::inner_join( more_phospho, more_proteome, by = c("Master.Protein.Accessions" = "Accession"))
  
  #Semi-join #semi_join() return all rows from x with a match in y. 
  more_phosphorylated <- dplyr::semi_join(more_phospho, more_proteome, by = c("Master.Protein.Accessions" = "Accession"))
  volcano_phospho_plot(more_phosphorylated, 2) # to control remaining data

  
remove(df_phospho, df_proteome, more_phospho, more_proteome, exclude_proteome)

Comparing data before and after the cleanup

-we could identify that the cleanup was conducted and not too stringent returning enough data to conduct analyisis on.


Volcano of data_phospho before filterZoomed in Volcano from more phosphorylated after filter


Building network map of the proteins

##Lower score_threshold, allowing more connections
string_db_low <- STRINGdb$new(version="11.5", species=10090, score_threshold=1, input_directory="") #species = mouse identifier, score threshold = if one interaction this will be mapped, no input directory data stored only temporary
hits_low <- string_db_low$map(more_phosphorylated, "Master.Protein.Accessions", removeUnmappedRows = F) #mapping the UNIPROT Ids from the first column to get STRING identifiers

string_db_low$plot_network(hits_low$STRING_id) #plotting network

invisible(string_db_low$get_png(hits_low$STRING_id, file = "~/phosphoproteomics_project/results/Network_low.png")) #Saving photo

## Higher score_threshold, allowing less connections
string_db_high <- STRINGdb$new(version="11.5", species=10090, score_threshold=200, input_directory="") #species = mouse identifier, score threshold = if one interaction this will be mapped, no input directory data stored only temporary
hits_high <- string_db_high$map(more_phosphorylated, "Master.Protein.Accessions", removeUnmappedRows = F) #mapping the UNIPROT Ids from the first column to get STRING identifiers

string_db_high$plot_network(hits_high$STRING_id) #plotting network

invisible(string_db_high$get_png(hits_high$STRING_id, file = "~/phosphoproteomics_project/results/Network_high.png")) #Saving photo

Way to identify the Proteins Stat5b is connected to.

#Finding String id identifier for Stat5b and assigning it to the a variable
i <- grep("P42232", hits_low$Master.Protein.Accessions) 
Stat5b <- string_db_low$mp("P42232") #mapping the UNIPROT Ids 

## Mapping Stat5b interactions in the less stringent data set and more stringent data set

#Defining a function to get 1. Proteins_connected_to_Stat5b, 2. Network plot, 3. Link to webpage, 4. Pathways
subnetworks <- function(data_h,score_h) {
  #a = The data we want to have a network from, b = the threshold number of the string_db function
  string_db <- STRINGdb$new(version="11.5", species=10090, score_threshold = score_h, input_directory="") #species = mouse 
  get <- string_db$get_subnetwork(data_h) #builds subnetwork from every entry in STRING_id
  ghi <- get[[Stat5b]][1] ##gives the list entry of "get" that lists all 18 entries that interact with Stat5b
  hg <- unique(ghi$`10090.ENSMUSP00000102981`) #gives the unique interaction (9 out of 18), 
  mat <- as_ids(ghi$`10090.ENSMUSP00000102981`) #function as_ids from the igraph package reads the class("igraph.vs")   and collapses it into a matrix
  Proteins_connected_to_Stat5b <- as.data.frame(unique(mat))
  names(Proteins_connected_to_Stat5b)[1] <- "STRING_id" #Renaming column so that add_protein_description recognizes it
  Proteins_connected_to_Stat5b[(nrow(Proteins_connected_to_Stat5b)+1),] <- Stat5b #Adds Stat5b to map its connections
  Proteins_connected_to_Stat5b <- string_db$add_proteins_description(Proteins_connected_to_Stat5b) #Add´s protein information to STRINGid´s
  
  #Plotting the subnetwork
  string_db$plot_network(Proteins_connected_to_Stat5b$STRING_id)
  
  #providing my link
  link <- string_db$get_link(Proteins_connected_to_Stat5b$STRING_id) #Provides link to the STRINGdb page
  
  #Providing the pathways
  pathways <- string_db$get_enrichment(Proteins_connected_to_Stat5b, category = "KEGG")

  return(list(Proteins_connected_to_Stat5b, pathways, link))
  
  remove(get,ghi,hg,mat)
   }

## Less stringent parameter to determine the Proteins that interact with Stat5
subnetwork_less_stringent <- subnetworks(data_h = hits_low$STRING_id, score_h = 1) #Using function
trying URL 'https://stringdb-static.org/download/protein.links.v11.5/10090.protein.links.v11.5.txt.gz'
Content type 'application/octet-stream' length 84569998 bytes (80.7 MB)
==================================================
downloaded 80.7 MB

#Sub-setting the returned list into original dataframes + link
Proteins_connected_to_Stat5b_less <- subnetwork_less_stringent[[1]]
pathways_less <- subnetwork_less_stringent[[2]]
Link_less <- subnetwork_less_stringent[[3]]
Link_less #Link to website
[1] "https://version-11-5.string-db.org/cgi/link?to=2E8CDC990DBD10F7"
invisible(string_db_low$get_png(Proteins_connected_to_Stat5b_less$STRING_id, file = "~/phosphoproteomics_project/results/Network_Stat5_less.png")) #Saving png

## More stringent parameter to determine the Proteins that interact with Stat5
subnetwork_more_stringent <- subnetworks(data_h = hits_high$STRING_id, score_h = 200) #Using function

#Sub-setting the returned list into original dataframes + link
Proteins_connected_to_Stat5b_more <- subnetwork_more_stringent[[1]] 
pathways_more <- subnetwork_more_stringent[[2]]
Link_more <- subnetwork_more_stringent[[3]]
Link_more #Link to website 
[1] "https://version-11-5.string-db.org/cgi/link?to=18B5C3E16E9B3AE0"
invisible(string_db_high$get_png(Proteins_connected_to_Stat5b_more$STRING_id, file = "~/phosphoproteomics_project/results/Network_Stat5_more.png")) #Saving png

Printing Pathways

-printing the enriched pathways from the subnetworks of Stat5b and renaming them for a better human readability & formatting of html document.

#Identifying only for Proteins connected to Stat5b

#Function to generate tabels based on factor gene counts / number of annotated genes.
factor_function <- function(Pathway){
i = 1
for (i in i:nrow(Pathway)) {
  Pathway$factor[i] <- round((Pathway[i,3]/Pathway[i,4]), digits = 3) #rounded for better visualization
}
Pathway <- rename(Pathway, background = number_of_genes_in_background)
Pathway <- rename(Pathway, hits = number_of_genes)
Pathway <- rename(Pathway, involved_genes = preferredNames)
Pathway <- Pathway[order(Pathway$factor,decreasing = T ),]
return(print.data.frame(Pathway[1:5,c(11,3,4,10,7)], row.names = F))
}
#Table for less stringent parameters
factor_function(pathways_less)
 factor hits background                 description            involved_genes
  0.054    4         74    Chronic myeloid leukemia Stat5a,Sos1,Ptpn11,Stat5b
  0.045    3         66  Non-small cell lung cancer        Stat5a,Sos1,Stat5b
  0.043    3         70      Acute myeloid leukemia        Stat5a,Sos1,Stat5b
  0.041    3         74 Prolactin signaling pathway        Stat5a,Sos1,Stat5b
  0.037    3         81      ErbB signaling pathway        Stat5a,Sos1,Stat5b
#Table for more stringent parameters
factor_function(pathways_more)
 factor hits background                                          description       involved_genes
  0.041    3         74                             Chronic myeloid leukemia Stat5a,Ptpn11,Stat5b
  0.030    3        101 AGE-RAGE signaling pathway in diabetic complications  Stat5a,Foxo1,Stat5b
  0.018    3        165                           JAK-STAT signaling pathway Stat5a,Ptpn11,Stat5b
     NA   NA         NA                                                 <NA>                 <NA>
     NA   NA         NA                                                 <NA>                 <NA>
#Identifying pathways also for Proteins with a ratio >=2 and p.value <= 0.05 and threshold = 200.
Big_network_pathways_more <- string_db_high$get_enrichment(hits_high$STRING_id, category = "KEGG")
factor_function(Big_network_pathways_more)
 factor hits background              description            involved_genes
  0.054    4         74 Chronic myeloid leukemia Stat5a,Sos1,Ptpn11,Stat5b
  0.049    4         81   ErbB signaling pathway   Stat5a,Gab1,Sos1,Stat5b
     NA   NA         NA                     <NA>                      <NA>
     NA   NA         NA                     <NA>                      <NA>
     NA   NA         NA                     <NA>                      <NA>
#Identifying pathways also for Proteins with a ratio >=2 and p.value <= 0.05 and threshold = 1.
Big_network_pathways_less <- string_db_low$get_enrichment(hits_low$STRING_id, category = "KEGG")
factor_function(Big_network_pathways_less)
 factor hits background              description            involved_genes
  0.054    4         74 Chronic myeloid leukemia Stat5a,Sos1,Ptpn11,Stat5b
  0.049    4         81   ErbB signaling pathway   Stat5a,Gab1,Sos1,Stat5b
     NA   NA         NA                     <NA>                      <NA>
     NA   NA         NA                     <NA>                      <NA>
     NA   NA         NA                     <NA>                      <NA>
remove(i, Link_less, Link_more, string_db_high, string_db_low, subnetworks, volcano_plot_proteome, subnetwork_less_stringent, subnetwork_more_stringent, hits_high, hits_low, data_phospho, factor_function, volcano_phospho_plot, string_db_volcano)

Results and Discussion

Conclusion



LS0tCnRpdGxlOiAiUGhvc3Bob3Byb3Rlb21pY3MiCm91dHB1dDoKICBwZGZfZG9jdW1lbnQ6IGRlZmF1bHQKICBodG1sX25vdGVib29rOiBkZWZhdWx0Ci0tLQoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIyMjIEludHJvZHVjdGlvbgoKLSAgIFRoZSByZXNlYXJjaCBxdWVzdGlvbiBjYW4gYmUgc3VtbWFyaXplZCBhczogIkRpc2VudGFuZ2xlIG1lY2hhbmlzbXMgb2YgY2VsbHVsYXIgZGlmZmVyZW50aWF0aW9uIGJldHdlZW4gcmVndWxhciBzdGVtIGNlbGxzIGFuZCBwbHVyaXBvdGVudCBzdGVtIGNlbGxzIGluIG1pY2UiCi0gICBUaGUgYXBwcm9hY2ggdXNlZCBpcyBwcm90ZW9taWNzIGJhc2VkIG9uIE1TIGRhdGEgdGhhdCB3YXMgZ2VuZXJhdGVkIGluIHR3byBkaWZmZXJlbnQgYmF0Y2hlcy4gVGhlIGRhdGFzZXRzIGFyZSBnZW5lcmF0ZWQgYnkgaW5kdWNpbmcgZGlmZmVyZW50aWF0aW9uIHdpdGggaG9yc2Utc2VydW0gb24gbm9uLWRpZmZlcmVudGlhdGVkIG11c2NsZSBtb3VzZSBjZWxscyAoQzJDMTIgY2VsbHMpIGFzIGNvbnRyb2wgYW5kIGJsb2NraW5nIHRoZSBkaWZmZXJlbnRpYXRpb24gYnkgTElGIGluIHRyZWF0ZWQgc2FtcGxlLiBQcm90ZW9tZSBkYXRhIHNldCBjb250YWlucyB0aHJlZSBjb250cm9sIHNhbXBsZXMgYW5kIHRocmVlIExJRiB0cmVhdGVkIHNhbXBsZXMgcGVyIGlkZW50aWZpZWQgcHJvdGVpbi4gVGhlIFBob3NwaG8gZGF0YSBzZXQgY29udGFpbnMgdGhlIHNhbWUgYW1vdW50IG9mIHNhbXBsZXMgZnJvbSBib3RoIGdyb3VwcywgYnV0IHRoZXNlIHdlcmUgZW5yaWNoZWQgZm9yIHBob3NwaG9yeWxhdGlvbi4gVGhpcyBpcyBhIGZvbGxvdyB1cCBleHBlcmltZW50LCBzaW5jZSB0aGUgZmlyc3QgZXhwZXJpbWVudCBjb250YWluaW5nIG9ubHkgdGhlIFByb3Rlb21lIGRhdGEgc2V0IHNob3dlZCBubyBzaWduaWZpY2FudCBpbmNyZWFzZSBmcm9tIGFueSBwcm90ZWluIGFuZCB0aGUgbmV3IGh5cG90aGVzaXMgaXMgYmFzZWQgb24gcG9zdC10cmFuc2xhdGlvbmFsIHBob3NwaG9yeWxhdGlvbiBhcyBzaWduYWwuCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMjIyMgRG93bmxvYWQgcmF3IGRhdGEgZnJvbSBVcHBtYXgKCmBgYHtiYXNofQoKI3JzeW5jIC1yIG5vYWhlQHJhY2toYW0udXBwbWF4LnV1LnNlOi9wcm9qL2cyMDIwMDA0L3ByaXZhdGUvc3R1ZGVudF9wcm9qZWN0cy9waG9zcGhvcHJvdGVvbWljcyAvaG9tZS9ub2FoL3Bob3NwaG9wcm90ZW9taWNzX3Byb2plY3QvCmBgYAoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIyMjIFNldHRpbmcgd29ya2luZyBkaXJlY3RvcnkgYW5kIGluc3RhbGwgbGlicmFyaWVzCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCByZXN1bHRzPSdoaWRlJ30Kc2V0d2QoIn4vcGhvc3Bob3Byb3Rlb21pY3NfcHJvamVjdCIpClJlcXVpcmVkUGFja2FnZXMgPC0gYygiQmlvY01hbmFnZXIiLCAiZHBseXIiLCAiaWdyYXBoIiwgInRpZHlyIiwiZ2dwbG90MiIsImdncmVwZWwiKSAKI0luc3RhbGxzIHBhY2thZ2VzIGlmIG5vdCB5ZXQgaW5zdGFsbGVkCmZvciAoaSBpbiBSZXF1aXJlZFBhY2thZ2VzKSB7IAogICAgaWYgKCFyZXF1aXJlKGksIGNoYXJhY3Rlci5vbmx5ID0gVFJVRSkpIAogICAgaW5zdGFsbC5wYWNrYWdlcyhpKQp9CkJpb2NNYW5hZ2VyOjppbnN0YWxsKHZlcnNpb24gPSAiMy4xNSIpCmxpYnJhcnkoQmlvY01hbmFnZXIpCkJpb2NNYW5hZ2VyOjppbnN0YWxsKCJTVFJJTkdkYiIpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoaWdyYXBoKQpsaWJyYXJ5KFNUUklOR2RiKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZ2dyZXBlbCkKcmVtb3ZlKFJlcXVpcmVkUGFja2FnZXMpCmBgYAoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIyMjIExvYWQgcmF3IGRhdGEKCi0gICBDaGFuZ2UgZGVmYXVsdCBzZXBhcmF0b3IgdG8gIjsiCgpgYGB7cn0KcmF3X2RhdGFfcGhvc3BobyA8LSByZWFkLmNzdihmaWxlID0gIi9ob21lL25vYWgvcGhvc3Bob3Byb3Rlb21pY3NfcHJvamVjdC9waG9zcGhvcHJvdGVvbWljcy9EYXRhc2V0X1Bob3NwaG8uY3N2Iiwgc2VwID0gIjsiKQpyYXdfZGF0YV9wcm90ZW9tIDwtIHJlYWQuY3N2KGZpbGUgPSAiL2hvbWUvbm9haC9waG9zcGhvcHJvdGVvbWljc19wcm9qZWN0L3Bob3NwaG9wcm90ZW9taWNzL0RhdGFzZXRfUHJvdGVvbWUuY3N2Iiwgc2VwID0gIjsiKQpgYGAKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyMjIyBJZiBsb29wIHRvIG9taXQgbmEudmFsdWUgZnJvbSBhbGwgQWJ1bmRhbmNlIGNvbHVtbnMKCi0gICBncmVwIGxpbmUgc2VsZWN0cyBhbGwgY29sdW1ucyB0aGF0IGNvbnRhaW4gdGhlIHN0cmluZyAiQWJ1bmRhbmNlIiBhbmQgc2F2ZXMgdGhlIGNvbHVtbiBudW1iZXIgaW4gKkFidW5kYW5jZV9jb2x1bW5zKgotICAgVGhlIGZvciBsb29wIHRoZW4gc2VsZWN0cyBhbGwgcm93IHdlcmUgdGhlcmUgaXMgKm5vKiBOQSB2YWx1ZSBpbiBvbmUgb2YgdGhlIEFidW5kYW5jZSBjb2x1bW5zIGFuZCBjb3BpZXMgdGhlbSB0byBkYXRhX3Bob3NwaG8gdG8gbGVhdmUgdGhlIHJhdyBkYXRhIHVudG91Y2hlZC4KLSAgICoqIShpcy5uYShyYXdfZGF0YV9waG9zcGhvWyxpXSkpKiogZ2l2ZXMgb3V0IGFsbCB0aGUgcm93cyB0aGF0IGRvbid0IGhhdmUgYW4gTkEgdmFsdWUsIHRoZXNlIGdldCBzZWxlY3RlZAoKYGBge3J9CkFidW5kYW5jZV9jb2x1bW5zIDwtIGdyZXAoIkFidW5kYW5jZSIsIGNvbG5hbWVzKHJhd19kYXRhX3Bob3NwaG8pICkKZm9yIChpIGluIEFidW5kYW5jZV9jb2x1bW5zKSB7CiAgZGF0YV9waG9zcGhvIDwtIHJhd19kYXRhX3Bob3NwaG9bIShpcy5uYShyYXdfZGF0YV9waG9zcGhvWyxpXSkpLCBdCiAgfQpgYGAKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyMjIyBDYWxjdWxhdGUgTWVhbiBmb3IgQWJ1bmRhbmNlIGFuZCBhZGRpbmcgaXQgYXMgY29sdW1uIGFuZCBSYXRpbyBMSUYvQ29udHJvbAoKLSAgIGdyZXAgYWdhaW4gQWJ1bmRhbmNlIGJ1dCB0aGlzIHRpbWUgdGhlIENvbnRyb2wgYW5kIExJRiAoY2FzZSkgc2VwYXJhdGVseS4KLSAgIGNhbGN1bGF0ZSByb3cgbWVhbnMgZWFjaCBhbmQgdGhlIHJhdGlvbiBiZXR3ZWVuIHRoZSBtZWFucy4KCmBgYHtyfQpBYnVuZGFuY2VfY29sdW1ucyA8LSBncmVwKCJBYnVuZGFuY2UuQ29udHJvbCIsIGNvbG5hbWVzKGRhdGFfcGhvc3BobykpCmRhdGFfcGhvc3BobyRBYnVkYW5jZS5NZWFuLkNvbnRyb2wgPC0gcm93TWVhbnMoZGF0YV9waG9zcGhvWyxjKEFidW5kYW5jZV9jb2x1bW5zKV0pCgpBYnVuZGFuY2VfY29sdW1ucyA8LSBncmVwKCJBYnVuZGFuY2UuTElGIiwgY29sbmFtZXMoZGF0YV9waG9zcGhvKSkKZGF0YV9waG9zcGhvJEFidWRhbmNlLk1lYW4uTElGIDwtIHJvd01lYW5zKGRhdGFfcGhvc3Bob1ssYyhBYnVuZGFuY2VfY29sdW1ucyldKQoKZGF0YV9waG9zcGhvJEFidWRhbmNlLlJhdGlvIDwtIGRhdGFfcGhvc3BobyRBYnVkYW5jZS5NZWFuLkxJRiAvIGRhdGFfcGhvc3BobyRBYnVkYW5jZS5NZWFuLkNvbnRyb2wKCnJlbW92ZShBYnVuZGFuY2VfY29sdW1ucywgaSkKYGBgCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMjIyMgQ2FsY3VsYXRlIHAtdmFsdWVzCgotICAgKnQudGVzdCogdG8gY29tcGFyZSBhbGwgMyBDb250cm9sIFw8LVw+IDMgQ2FzZSB2YWx1ZXMuIElmIHRoZXkgYXJlIGRpZmZlcmVudCB0aGVuIHRoZSBwdmFsdWUgXDwgMC4wNSAqKi1cPioqIGluZGljYXRpbmcgYSByZWFsIGRpZmZlcmVuY2VzIGJldHdlZW4gY2FzZSAmIGNvbnRyb2wuIElmIHAtdmFsdWUgaXMgYmlnIHRoZSBkaWZmZXJlbmNlIGlzIGJ5IGNoYW5jZfCfmIQgSSB0cmllZCBpdCBmb3Igcm93IDE4OCB3aGljaCBoYXMgYSBSYXRpb24gbmVhcmx5IGVxdWFsIHRvIDEgKDEuMDIuLi4pIGluZGljYXRpbmcgbm8gZGlmZmVyZW5jZSBpbiBjYXNlIHZzLiBjb250cm9sIGFuZCB0aGUgcCB2YWx1ZSBcfiAwLjcgKHJlYWxseSBiYWQpIGFsc28gaW5kaWNhdGluZyBubyBkaWZmZXJlbmNlLiBUaGUgZm9yIGxvb3AgaXRlcmF0ZXMgaXQgb3ZlciBhbGwgcm93cywgQWxpY2VzIHF1aWNrZXIgYXBwcm9hY2ggdXNlcyB0aGUgYXBwbHkgZnVuY3Rpb24gdGhhdCBhbGxvd3MgcGFyYWxsZWwgaXRlcmF0aW9uIG1ha2luZyBpdCBxdWlja2VyLgoKYGBge3Igd2FybmluZz1GQUxTRX0Kc3RhcnRfZm9yIDwtIFN5cy50aW1lKCkKZm9yIChyb3cgaW4gMTpucm93KGRhdGFfcGhvc3BobykpIHsKICBwX3ZhbHVlIDwtIHQudGVzdChkYXRhX3Bob3NwaG9bcm93LDEwOjEyXSwgZGF0YV9waG9zcGhvW3JvdywxMzoxNV0sIGFsdGVybmF0aXZlID0gImxlc3MiKSAKICAjdGVzdHMgaWYgbWVhbiBvZiBjb250cm9sIGlzIGRpZmZlcmVudCBmcm9tIG1lYW4gb2YgTElGIChjYXNlKSBhbmQgYXNzaWducyBpdCB0byBsaXN0IHBfdmFsdWUKICBkYXRhX3Bob3NwaG8kQWJ1ZGFuY2UucHZhbHVlW3Jvd10gPC0gcF92YWx1ZSRwLnZhbHVlIAogICNzZWxlY3RzIHAudmFsdWUgZnJvbSBsaXN0IGFuZCBhc3NpZ25zIGl0IHRvIG5ldyBjb2x1bW4iQWJ1ZGFuY2UucHZhbHVlIiBpbiB0aGUgY29ycmVjdCByb3cKICB9CmVuZF9mb3IgPC0gU3lzLnRpbWUoKQplbmRfZm9yIC0gc3RhcnRfZm9yICNXZSB3YW50ZWQgdG8gY29tcGFyZSBjYWxjdWxhdGlvbiB0aW1lIHRvIGNvbXBhcmUgdGhlIG1ldGhvZHMKCiNBbGljZSBxdWlja2VyIGFwcHJvYWNoCmRmX2NvcHkgPC0gZHBseXI6OnRpYmJsZShkYXRhX3Bob3NwaG8pICMgQ29weSBkZgpzdGFydF9hbGljZSA8LSBTeXMudGltZSgpCmRmX2NvcHlbXSA8LSBsYXBwbHkoZGZfY29weSwgZnVuY3Rpb24oeCkgYXMubnVtZXJpYyhhcy5jaGFyYWN0ZXIoeCkpKQpkZl9jb3B5JHBWYWx1ZXMgPC0gYXBwbHkoZGZfY29weSwgMSwgZnVuY3Rpb24oeCkgdC50ZXN0KHhbMTA6MTJdLHhbMTM6MTVdLCBhbHRlcm5hdGl2ZSA9ICJsZXNzIiwgcGFpcmVkID0gRikkcC52YWx1ZSkKZW5kX2FsaWNlIDwtIFN5cy50aW1lKCkKZW5kX2FsaWNlLXN0YXJ0X2FsaWNlICNQcm92ZXMgdGhhdCBhcHBseSBpcyB4NCBmYXN0ZXIgdGhlbiBsb29wCnJlbW92ZShlbmRfYWxpY2UsZW5kX2ZvcixzdGFydF9hbGljZSxzdGFydF9mb3Iscm93LHBfdmFsdWUsIGRmX2NvcHkpCmBgYAoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIyMjIEFkZGluZyBhIFZvbGNhbm8gcGxvdCB0byBleHBsb3JlIHRoZSBpbml0aWFsIGRhdGEKCi0gICBUaGUgbWFpbiBpZGVhIGlzIHRvIGlkZW50aWZ5IHRocmVzaG9sZHMgdGhhdCBjYW4gYmUgdXNlZCB0byBjbGVhbiB0aGUgcmF3IGRhdGEuCgpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQojIyMgUGhvc3BobyB2b2xjYW5vIHBsb3QKc3RyaW5nX2RiX3ZvbGNhbm8gPC0gU1RSSU5HZGIkbmV3KCB2ZXJzaW9uPSIxMS41Iiwgc3BlY2llcz0xMDA5MCwgc2NvcmVfdGhyZXNob2xkPTEsIGlucHV0X2RpcmVjdG9yeT0iIikKIyBUaGlzIGhhZCB0byBiZSBvdXRzaWRlIHRoZSB2b2xjYW5vX3Bob3NwaG9fcGxvdCBmdW5jdGlvbiwgYmVjYXVzZSB0aGUgZnVuY3Rpb24gZGlkbnQgcmV0dXJuIHRoZSBzdHJpbmdfZGJfdm9sY2FubyBmdW5jdGlvbiB0aGF0IGlzIG5lZWRlZCBhbHNvIGFmdGVyd2FyZHMuCgojIERlZmluaW5nIGZ1bmN0aW9uIHRvIHBsb3QgZGF0YQogICAgIyBNYWtlcyB0aGUgd2hvbGUgY29kZSBlYXN5IHRvIHJldXNlLgoKdm9sY2Fub19waG9zcGhvX3Bsb3QgPC0gZnVuY3Rpb24ocGhvc3BobywgbnVtYmVyKXsKCiMgQ2FsY3VsYXRlIExJRi9Db250cm9sIHJhdGlvCnBob3NwaG8kRkMgPC0gcGhvc3BobyRBYnVkYW5jZS5NZWFuLkxJRiAvIHBob3NwaG8kQWJ1ZGFuY2UuTWVhbi5Db250cm9sCnBob3NwaG8kbG9nMl9GQyA8LSBsb2cyKHBob3NwaG8kRkMpCnBob3NwaG8kZGlmZmV4cHJlc3NlZCA8LSAiTk8iCnBob3NwaG8kZGlmZmV4cHJlc3NlZFtwaG9zcGhvJGxvZzJfRkMgPiAxICYgcGhvc3BobyRBYnVkYW5jZS5wdmFsdWUgPCAwLjA1XSA8LSAiVVAiICMgc29ydHMgb3V0IHVwcmVndWxhdGVkIHBob3NwbyB2YWx1ZXMgYW5kIGFzc2lnbnMgIlVQIiB2YWx1ZS4KcGhvc3BobyRkaWZmZXhwcmVzc2VkW3Bob3NwaG8kbG9nMl9GQyA8IC0xICYgcGhvc3BobyRBYnVkYW5jZS5wdmFsdWUgPCAwLjA1XSA8LSAiRE9XTiIgIyBzYW1lIG9yIGRvd25yZWd1bGF0ZWQgcGhvc3BobyB2YWx1ZXMKCnBob3NwaG8kZGVsYWJlbCA8LSBOQQpwaG9zcGhvJGRlbGFiZWxbcGhvc3BobyRkaWZmZXhwcmVzc2VkICE9ICJOTyJdIDwtIHBob3NwaG8kTWFzdGVyLlByb3RlaW4uQWNjZXNzaW9uc1twaG9zcGhvJGRpZmZleHByZXNzZWQgIT0gIk5PIl0gIyBzZWxlY3Rpbmcgb25seSB1cCBvciBkb3duIHJlZ3VsYXRlZCB2YWx1ZXMgYW5kIGFzc2lnbmluZyB0aGVtIHRvIHRoZSBsYWJlbCBjb2x1bW4uCgojI0FkZGl0aW9uIHRvIGdlbmVyYXRlICJyZWFsIG5hbWVzIiBub3QgVW5pcHJvdCBJRHMKTWFwcGluZ19waG9zcGhvIDwtIHN0cmluZ19kYl92b2xjYW5vJG1hcChwaG9zcGhvLCAiZGVsYWJlbCIsIHJlbW92ZVVubWFwcGVkUm93cyA9IEYpCk1hcHBpbmdfcGhvc3BobyA8LSBzdHJpbmdfZGJfdm9sY2FubyRhZGRfcHJvdGVpbnNfZGVzY3JpcHRpb24oTWFwcGluZ19waG9zcGhvKSAjQWRkwrRzIHByb3RlaW4gaW5mb3JtYXRpb24gdG8gU1RSSU5HaWTCtHMgPSBwcmVmZXJyZWQgbmFtZXMgYXJlIG1vcmUgaHVtYW4gcmVhZGFibGUKCiNWb2xjYW5vIHBsb3Qgb3ZlcnZpZXcKZ2dwbG90KGRhdGE9TWFwcGluZ19waG9zcGhvLCBhZXMoeD1sb2cyX0ZDLCB5PS1sb2cxMChBYnVkYW5jZS5wdmFsdWUpLCBjb2w9ZGlmZmV4cHJlc3NlZCwgbGFiZWw9cHJlZmVycmVkX25hbWUpKSsKICBnZW9tX3BvaW50KCkrCiAgdGhlbWVfbWluaW1hbCgpKwogIGdlb21fdmxpbmUoeGludGVyY2VwdD1jKC0xLCAxKSwgY29sPSJyZWQiKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0PS1sb2cxMCgwLjA1KSwgY29sPSJyZWQiKQojc2F2ZXMgdGhlIGNvcnJlY3QgcGhvdG8gZm9yIGJvdGggcG9zc2libGUgaW5wdXQgdGFibGVzCiMgbnVtYmVyIDEgb3IgMiBoYXMgdG8gYmUgZGVmaW5lZCBpbiB0aGUgZnVuY3Rpb24gaW5wdXQgZGF0YS4gVGhpcyBpcyBhIGJpdCBjbHVtc3kgc29sdmVkLCBidXQgdGhlcmUgd2VyZSBwcm9ibGVtcyB3aXRoIHJldHVybmluZyBtb3JlIHRoYW4gb25lIHBsb3Qgb3V0IG9mIGEgZnVuY3Rpb24uCmlmIChudW1iZXIgPT0gMSkgewogIGdnc2F2ZSgifi9waG9zcGhvcHJvdGVvbWljc19wcm9qZWN0L3Jlc3VsdHMvWm9vbWVkX291dF9Wb2xjYW5vLnBuZyIsIHdpZHRoID0gMTUwMCwgaGVpZ2h0ID0gMTAwMCwgdW5pdHMgPSAicHgiKSAjIFNhdmVzIHBsb3RzIHdpdGggZGVmaW5lZCBzaXplCn0KaWYgKG51bWJlciA9PSAyKSB7CiAgZ2dzYXZlKCJ+L3Bob3NwaG9wcm90ZW9taWNzX3Byb2plY3QvcmVzdWx0cy9ab29tZWRfb3V0X1ZvbGNhbm9fZm9yX21vcmVfcGhvc3BocnlsYXRlZF9hZnRlcl9xY19hbmRfZmlsdGVyLnBuZyIsIHdpZHRoID0gMTUwMCwgaGVpZ2h0ID0gMTAwMCwgdW5pdHMgPSAicHgiKQp9CiNEZXRhaWwvWm9vbWVkIGluCmdncGxvdChkYXRhPU1hcHBpbmdfcGhvc3BobywgYWVzKHg9bG9nMl9GQywgeT0tbG9nMTAoQWJ1ZGFuY2UucHZhbHVlKSwgY29sPWRpZmZleHByZXNzZWQsIGxhYmVsPXByZWZlcnJlZF9uYW1lKSkrCiAgZ2VvbV9wb2ludCgpKwogIHRoZW1lX21pbmltYWwoKSsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQ9YygtMSwgMSksIGNvbD0icmVkIikgKwogIGdlb21faGxpbmUoeWludGVyY2VwdD0tbG9nMTAoMC4wNSksIGNvbD0icmVkIikgKyAKICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoMSwzLjUpLCB5bGltID0gYygwLjUsIDYpKSsKICBnZW9tX2xhYmVsX3JlcGVsKCkKCiNzYXZlcyB0aGUgY29ycmVjdCBwaG90byBmb3IgYm90aCBwb3NzaWJsZSBpbnB1dCB0YWJsZXMKCmlmIChudW1iZXIgPT0gMSkgewogIGdnc2F2ZSgifi9waG9zcGhvcHJvdGVvbWljc19wcm9qZWN0L3Jlc3VsdHMvWm9vbWVkX2luX1ZvbGNhbm8ucG5nIiwgd2lkdGggPSAxNTAwLCBoZWlnaHQgPSAxMDAwLCB1bml0cyA9ICJweCIpCn0KaWYgKG51bWJlciA9PSAyKSB7Cmdnc2F2ZSgifi9waG9zcGhvcHJvdGVvbWljc19wcm9qZWN0L3Jlc3VsdHMvWm9vbWVkX2luX1ZvbGNhbm9fZm9yX21vcmVfcGhvc3BocnlsYXRlZF9hZnRlcl9xY19hbmRfZmlsdGVyLnBuZyIsIHdpZHRoID0gMTUwMCwgaGVpZ2h0ID0gMTAwMCwgdW5pdHMgPSAicHgiKQp9Cn0KCnZvbGNhbm9fcGhvc3Bob19wbG90KGRhdGFfcGhvc3BobywgMSkgI3Bsb3RzIGRhdGEgdG8gY29udHJvbAoKCmBgYAoKIVtPdmVyd2lldiBWb2xjYW5vIFBob3Nob10oaW1hZ2VzL1pvb21lZF9vdXRfVm9sY2Fuby5wbmcpCgohW1pvb21lZCBpbiBWb2xjYW5vIHBsb3QgUGhvc3Bob10oaW1hZ2VzL1pvb21lZF9pbl9Wb2xjYW5vLnBuZykKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyMjIyBQcm90ZW9tZSB2b2xjYW5vIHBsb3QKCi0gICBBbHNvIHBsb3R0aW5nIHRoZSBwcm90ZW9tZSBkYXRhIHRvIGlkZW50aWZ5IHRocmVzaG9sZHMgdG8gY2xlYW4gZGF0YQoKYGBge3J9CiMgRGVmaW5pbmcgZnVuY3Rpb24gZmlyc3QKdm9sY2Fub19wbG90X3Byb3Rlb21lIDwtIGZ1bmN0aW9uKHByb3Rlb21lKXsKcHJvdGVvbWUkbG9nMl9GQyA8LSBsb2cyKHByb3Rlb21lJEFidW5kYW5jZS5SYXRpby4uLkxJRi4uLi4uQ29udHJvbC4pCnByb3Rlb21lJGRpZmZleHByZXNzZWQgPC0gIk5PIgpwcm90ZW9tZSRkaWZmZXhwcmVzc2VkW3Byb3Rlb21lJGxvZzJfRkMgPiAxICYgcHJvdGVvbWUkQWJ1bmRhbmNlLlJhdGlvLlAuVmFsdWUuLi5MSUYuLi4uLkNvbnRyb2wuIDwgMC4wNV0gPC0gIlVQIgpwcm90ZW9tZSRkaWZmZXhwcmVzc2VkW3Byb3Rlb21lJGxvZzJfRkMgPCAtMSAmIHByb3Rlb21lJEFidW5kYW5jZS5SYXRpby5QLlZhbHVlLi4uTElGLi4uLi5Db250cm9sLiA8IDAuMDVdIDwtICJET1dOIgoKZ2dwbG90KGRhdGE9cHJvdGVvbWUsIGFlcyh4PWxvZzJfRkMsIHk9LWxvZzEwKEFidW5kYW5jZS5SYXRpby5QLlZhbHVlLi4uTElGLi4uLi5Db250cm9sLiksIGNvbD1kaWZmZXhwcmVzc2VkKSkrCiAgZ2VvbV9wb2ludCgpKwogIHRoZW1lX21pbmltYWwoKSsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQ9YygtMSwgMSksIGNvbD0icmVkIikgKwogIGdlb21faGxpbmUoeWludGVyY2VwdD0tbG9nMTAoMC4wNSksIGNvbD0icmVkIikKfQojUHJpbnRpbmcgdGhlIHZvbGNhbm8gcGxvdAp2b2xjYW5vX3Bsb3RfcHJvdGVvbWUocmF3X2RhdGFfcHJvdGVvbSkgIyBQbG90cyBhbGwgdGhlIGRhdGEgCgpgYGAKCi0gICBObyB2YWx1ZXMgYXJlIHNpZ25pZmljYW50bHkgdXAgb3IgZG93biByZWd1bGF0ZWQgKGRlZmluaW5nIHVwIC8gZG93biByZWd1bGF0ZWQgYXMgYXQgbGVhc3QgMi1mb2xkIGluY3JlYXNlL2RlY3JlYXNlKS4gQWxzbyBvdXRsaWVycyBjb3VsZCBiZSBvYnNlcnZlZCAoNCB2YWxlcyB3aXRoIGEgXD4yLWZvbGQgaW5jcmVhc2Ugd2l0aCBub24tc2lnbmlmaWNhbnQgcC12YWx1ZXMpLiBUbyBiZSBtb3JlIHJlc3RyaWN0aXZlIGFuZCBkZWNyZWFzZSB0aGUgY2hhbmdlIG9mIGZhbHNlIHBvc2l0aXZlcyBpbiB0aGUgdXBjb21pbmcgYW5hbHlzaXMsIGFsc28gc2lnbmlmaWNhbnRseSBpbmNyZWFzZWQgdmFsdWVzIHdpdGggYSByYXRpbyBcPiAxIHdlcmUgc2VsZWN0ZWQgYXMgdW5kZXNpcmVkIGRhdGEgYW5kIGFyZSBnb2luZyB0byBiZSByZW1vdmVkIGluIHRoZSBmb2xsb3dpbmcgY2xlYW5pbmcgc3RlcHMuCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMjIyMgU3Vic2V0dGluZyBkYXRhX3Bob3NwaG8gZm9yIHJhdGlvIHZhbHVlcyBcPj0gMiBhbmQgcC12YWx1ZXMgXDw9IDAuMDUKCi0gICAKCiAgICBhLiAgUmVtb3ZlIHBlcHRpZGVzIHdpdGhvdXQgcXVhbnRpdGF0aXZlIHZhbHVlcyAtXD4gQWxyZWFkeSBkb25lIGluICJJZiBsb29wIHRvIG9taXQgbmEudmFsdWUgZnJvbSBhbGwgQWJ1bmRhbmNlIGNvbHVtbnMiIHRoZXJlYnkgY3JlYXRpbmcgdGhlIGRhdGFfcGhvc3BobwoKLSAgIEhlcmUgd2UgdXNlIHRoZSBpZGVudGlmaWVkIHRocmVzaG9sZHMgZnJvbSB0aGUgdm9sY2FubyBwbG90cyB0byBjbGVhbiB0aGUgZGF0YS4gRmlyc3Qgd2Ugc2VsZWN0IHJvd3Mgd2l0aCByYXRpbyBcPj0gMiBhbmQgcC52YWx1ZXMgXDw9IDAuMDUgZnJvbSB0aGUgcGhvcGhvIGRhdGFzZXQuIEZyb20gdGhlIHByb3Rlb20gZGF0YSBzZXQgdmFsdWVzIHdlcmUgc2VsZWN0ZWQgd2l0aCBhIGFsbCByb3dzIHdpdGggYSByYXRpbyBcPD0gMSwgYWxsIHJvd3Mgd2l0aCBhIHJhdGlvIFw+PSAxIFw+PSAyIGFuZCBwLnZhbHVlIFw+PSAwLjA1IChzaWduaWZpY2FudCBwLXZhbHVlcyBhcmUgZXhjbHVkZWQgYW5kIHJhdGlvcyBcPiBiaWdnZXIgdGhhbiAyIHRvbykuIEFmdGVyIHRoaXMgaW5pdGlhbCBjbGVhbnVwIGEgc2VtaV9qb2luKCkgd2FzIHBlcmZvcm1lZCByZXR1cm4gYWxsIHJvd3MgZnJvbSBjbGVhbnVwIHBob3NwaG8gd2l0aCBhIG1hdGNoIGluIGNsZWFudXAgcHJvdGVvbWUsIHRvIHNlbGVjdCBvbmx5IHBob3NwbyB2YWx1ZXMgdGhhdCBzaG93ZWQgaW5jcmVhc2VkIHBob3NwaG9yeWxhdGlvbiBkdWUgdG8gdGhlIExJRiB0cmVhdG1lbnQuCgotICAgdm9sY2FubyBwbG90cyB3ZXJlIHVzZWQgdG8gdmlzdWFsaXplIHRoZSBjbGVhbmluZy9zZWxlY3Rpbmcgc3RlcHMKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiMjRmlsdGVyIE5BIG91dCBpbiBwcm90ZW9tZSwgRmlsdGVyIG91dCBwLnZhbHVlcyB0aGF0IGFyZSBub24gc2lnbmlmaWNhbnQobm90IGFkanVzdGVkKQogICNzZWN1cmluZyBubyBvdmVyd3JpdGUgdG8gb3JpZ2luYWwgZGF0YQpkZl9waG9zcGhvID0gZGF0YV9waG9zcGhvCmRmX3Byb3Rlb21lID0gcmF3X2RhdGFfcHJvdGVvbQogIAojIyBGaWx0ZXIgUEhPU1BITwogIG1vcmVfcGhvc3BobyA8LSBzdWJzZXQuZGF0YS5mcmFtZShkZl9waG9zcGhvLCBzdWJzZXQgPSBkZl9waG9zcGhvJEFidWRhbmNlLlJhdGlvID49IDIpICNGaWx0ZXJzIG91dCBhbGwgcmF0aW9zIGJlbG93IDItZm9sZAogIG1vcmVfcGhvc3BobyA8LSBzdWJzZXQuZGF0YS5mcmFtZShtb3JlX3Bob3NwaG8sIHN1YnNldCA9IG1vcmVfcGhvc3BobyRBYnVkYW5jZS5wdmFsdWUgPD0gMC4wNSkgI0ZpbHRlcnMgb3V0IGFsbCBub24tc2lnbmlmaWNhbnQgcC12YWx1ZXMKICAKICAjI0ZpbHRlciBQUk9URU9NRQogIGV4Y2x1ZGVfcHJvdGVvbWUgPC0gZmlsdGVyKGRmX3Byb3Rlb21lLCBkZl9wcm90ZW9tZSRBYnVuZGFuY2UuUmF0aW8uUC5WYWx1ZS4uLkxJRi4uLi4uQ29udHJvbC4gPD0gMC4wNSAmIGRmX3Byb3Rlb21lJEFidW5kYW5jZS5SYXRpby4uLkxJRi4uLi4uQ29udHJvbC4gPj0gMSkgI0ZpbHRlcnMgb3V0IGFsbCBzaWduaWZpY2FudCBwLXZhbHVlcywgYmVjYXVzZSBzaWduaWZpY2FudCBwLXZhbHVlcyBpbmRpY2F0ZSBhbiAKICAjRGF0YSB0aGF0IHdlIHdhbnQgdG8gZXhjbHVkZSwgdGVzdGluZyBpZiB0aGUgcmlnaHQgZGF0YSBpcyBzZWxlY3RlZCB3aXRoIHRoZSB2b2xjYW5vIHBsb3QKICB2b2xjYW5vX3Bsb3RfcHJvdGVvbWUoZXhjbHVkZV9wcm90ZW9tZSkKICAjQW50aV9qb2luIGFudGlfam9pbigpIHJldHVybiBhbGwgcm93cyBmcm9tIHggd2l0aG91dCBhIG1hdGNoIGluIHkuIAogIG1vcmVfcHJvdGVvbWUgPC0gYW50aV9qb2luKGRmX3Byb3Rlb21lLCBleGNsdWRlX3Byb3Rlb21lLCBieSA9IGMoIkFjY2Vzc2lvbiIgPSAiQWNjZXNzaW9uIikpCiAgbW9yZV9wcm90ZW9tZSA8LSBmaWx0ZXIobW9yZV9wcm90ZW9tZSwgbW9yZV9wcm90ZW9tZSRBYnVuZGFuY2UuUmF0aW8uLi5MSUYuLi4uLkNvbnRyb2wuIDw9IDIpICMgcmVtb3ZlcyBvdXRsaWVycyB3aXRoIHJhdGlvIGJpZ2dlciB0aGVuIDIKICAjQ2hlY2tpbmcgdGhlIGlmIHRoZSByaWdodCBkYXRhIGlzIGV4Y2x1ZGVkCiAgdm9sY2Fub19wbG90X3Byb3Rlb21lKG1vcmVfcHJvdGVvbWUpCiAgCiAgI0lubmVyIGpvaW4gd29ya3MgdG9vLCBidXQgc2VtaSBqb2luIGlzIGJldHRlciBiZWNhc3VlIHRoZSBwcm90ZW9tZSBjb2x1bW5zIGFyZSBub3QgaW5jbHVkZWQgaW50byB0aGUgcmVzdWx0aW5nIGRhdGEgZnJhbWUuCiAgI21vcmVfcGhvc3Bob3J5bGF0ZWQgPC0gZHBseXI6OmlubmVyX2pvaW4oIG1vcmVfcGhvc3BobywgbW9yZV9wcm90ZW9tZSwgYnkgPSBjKCJNYXN0ZXIuUHJvdGVpbi5BY2Nlc3Npb25zIiA9ICJBY2Nlc3Npb24iKSkKICAKICAjU2VtaS1qb2luICNzZW1pX2pvaW4oKSByZXR1cm4gYWxsIHJvd3MgZnJvbSB4IHdpdGggYSBtYXRjaCBpbiB5LiAKICBtb3JlX3Bob3NwaG9yeWxhdGVkIDwtIGRwbHlyOjpzZW1pX2pvaW4obW9yZV9waG9zcGhvLCBtb3JlX3Byb3Rlb21lLCBieSA9IGMoIk1hc3Rlci5Qcm90ZWluLkFjY2Vzc2lvbnMiID0gIkFjY2Vzc2lvbiIpKQogIHZvbGNhbm9fcGhvc3Bob19wbG90KG1vcmVfcGhvc3Bob3J5bGF0ZWQsIDIpICMgdG8gY29udHJvbCByZW1haW5pbmcgZGF0YQogIApyZW1vdmUoZGZfcGhvc3BobywgZGZfcHJvdGVvbWUsIG1vcmVfcGhvc3BobywgbW9yZV9wcm90ZW9tZSwgZXhjbHVkZV9wcm90ZW9tZSkKCmBgYAoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIyMjIENvbXBhcmluZyBkYXRhIGJlZm9yZSBhbmQgYWZ0ZXIgdGhlIGNsZWFudXAKCi13ZSBjb3VsZCBpZGVudGlmeSB0aGF0IHRoZSBjbGVhbnVwIHdhcyBjb25kdWN0ZWQgYW5kIG5vdCB0b28gc3RyaW5nZW50IHJldHVybmluZyBlbm91Z2ggZGF0YSB0byBjb25kdWN0IGFuYWx5aXNpcyBvbi4KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyMjIyAhW1ZvbGNhbm8gb2YgZGF0YV9waG9zcGhvIGJlZm9yZSBmaWx0ZXJdKGltYWdlcy9ab29tZWRfaW5fVm9sY2Fuby0wMS5wbmcpe3dpZHRoPSIzNTAifSFbWm9vbWVkIGluIFZvbGNhbm8gZnJvbSBtb3JlIHBob3NwaG9yeWxhdGVkIGFmdGVyIGZpbHRlcl0oaW1hZ2VzL1pvb21lZF9pbl9Wb2xjYW5vX2Zvcl9tb3JlX3Bob3NwaHJ5bGF0ZWRfYWZ0ZXJfcWNfYW5kX2ZpbHRlci5wbmcpe3dpZHRoPSIzNTAifQoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIyMjIAoKIyMjIyBCdWlsZGluZyBuZXR3b3JrIG1hcCBvZiB0aGUgcHJvdGVpbnMKCi0gICBXaXRoIGhlbHAgb2YgdGhlIFNUUklOZ2RiIHBhY2thZ2UgdGhlIG92ZXJ2aWV3IG5ldHdvcmtzIHdlcmUgcHJvZHVjZWQgd2l0aCBkaWZmZXJlbnQgdGhyZXNob2xkcy4KCmBgYHtyfQojI0xvd2VyIHNjb3JlX3RocmVzaG9sZCwgYWxsb3dpbmcgbW9yZSBjb25uZWN0aW9ucwpzdHJpbmdfZGJfbG93IDwtIFNUUklOR2RiJG5ldyh2ZXJzaW9uPSIxMS41Iiwgc3BlY2llcz0xMDA5MCwgc2NvcmVfdGhyZXNob2xkPTEsIGlucHV0X2RpcmVjdG9yeT0iIikgI3NwZWNpZXMgPSBtb3VzZSBpZGVudGlmaWVyLCBzY29yZSB0aHJlc2hvbGQgPSBpZiBvbmUgaW50ZXJhY3Rpb24gdGhpcyB3aWxsIGJlIG1hcHBlZCwgbm8gaW5wdXQgZGlyZWN0b3J5IGRhdGEgc3RvcmVkIG9ubHkgdGVtcG9yYXJ5CmhpdHNfbG93IDwtIHN0cmluZ19kYl9sb3ckbWFwKG1vcmVfcGhvc3Bob3J5bGF0ZWQsICJNYXN0ZXIuUHJvdGVpbi5BY2Nlc3Npb25zIiwgcmVtb3ZlVW5tYXBwZWRSb3dzID0gRikgI21hcHBpbmcgdGhlIFVOSVBST1QgSWRzIGZyb20gdGhlIGZpcnN0IGNvbHVtbiB0byBnZXQgU1RSSU5HIGlkZW50aWZpZXJzCgpzdHJpbmdfZGJfbG93JHBsb3RfbmV0d29yayhoaXRzX2xvdyRTVFJJTkdfaWQpICNwbG90dGluZyBuZXR3b3JrCmludmlzaWJsZShzdHJpbmdfZGJfbG93JGdldF9wbmcoaGl0c19sb3ckU1RSSU5HX2lkLCBmaWxlID0gIn4vcGhvc3Bob3Byb3Rlb21pY3NfcHJvamVjdC9yZXN1bHRzL05ldHdvcmtfbG93LnBuZyIpKSAjU2F2aW5nIHBob3RvCgojIyBIaWdoZXIgc2NvcmVfdGhyZXNob2xkLCBhbGxvd2luZyBsZXNzIGNvbm5lY3Rpb25zCnN0cmluZ19kYl9oaWdoIDwtIFNUUklOR2RiJG5ldyh2ZXJzaW9uPSIxMS41Iiwgc3BlY2llcz0xMDA5MCwgc2NvcmVfdGhyZXNob2xkPTIwMCwgaW5wdXRfZGlyZWN0b3J5PSIiKSAjc3BlY2llcyA9IG1vdXNlIGlkZW50aWZpZXIsIHNjb3JlIHRocmVzaG9sZCA9IGlmIG9uZSBpbnRlcmFjdGlvbiB0aGlzIHdpbGwgYmUgbWFwcGVkLCBubyBpbnB1dCBkaXJlY3RvcnkgZGF0YSBzdG9yZWQgb25seSB0ZW1wb3JhcnkKaGl0c19oaWdoIDwtIHN0cmluZ19kYl9oaWdoJG1hcChtb3JlX3Bob3NwaG9yeWxhdGVkLCAiTWFzdGVyLlByb3RlaW4uQWNjZXNzaW9ucyIsIHJlbW92ZVVubWFwcGVkUm93cyA9IEYpICNtYXBwaW5nIHRoZSBVTklQUk9UIElkcyBmcm9tIHRoZSBmaXJzdCBjb2x1bW4gdG8gZ2V0IFNUUklORyBpZGVudGlmaWVycwoKc3RyaW5nX2RiX2hpZ2gkcGxvdF9uZXR3b3JrKGhpdHNfaGlnaCRTVFJJTkdfaWQpICNwbG90dGluZyBuZXR3b3JrCmludmlzaWJsZShzdHJpbmdfZGJfaGlnaCRnZXRfcG5nKGhpdHNfaGlnaCRTVFJJTkdfaWQsIGZpbGUgPSAifi9waG9zcGhvcHJvdGVvbWljc19wcm9qZWN0L3Jlc3VsdHMvTmV0d29ya19oaWdoLnBuZyIpKSAjU2F2aW5nIHBob3RvCmBgYAoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIyMjIFdheSB0byBpZGVudGlmeSB0aGUgUHJvdGVpbnMgU3RhdDViIGlzIGNvbm5lY3RlZCB0by4KCi0gICBJbiB0aGlzIGNodW5rIHdlIGRlZmluZSBhIGZ1bmN0aW9uIHdpdGggYSBtb3JlIGFuZCBhIGxlc3Mgc3RyaW5nZW50IHRocmVzaG9sZCB0byBnZXQgMS4gUHJvdGVpbnNfY29ubmVjdGVkX3RvX1N0YXQ1YiBmb3IgbW9yZSBvciBsZXNzIHN0cmluZ2VudCB0aHJlc2hvbGQsIDIuIE5ldHdvcmsgcGxvdCBmb3IgdGhlIHN1Ym5ldHdvcmtzIG9mIHByb3RlaW5zIGNvbm5lY3RlZCB0byBTdGF0NWIsIDMuIExpbmsgdG8gd2VicGFnZSBmb3IgdGhlIHN1Ym5ldHdvcmtzLCA0LiBQYXRod2F5cyB0aGF0IGFyZSBlbnJpY2hlZCBpbiB0aGUgc3VibmV0d29ya3MuCgotICAgVGhlcmUgaXMgYWxzbyBhIGxpbmsgdG8gdGhlIFtTVFJJTkdkYiB3ZWJzaXRlIGZvciBsZXNzIHN0cmluZ2VudCB0aHJlc2hvbGQgPSAxXShodHRwczovL3ZlcnNpb24tMTEtNS5zdHJpbmctZGIub3JnL2NnaS9saW5rP3RvPTJFOENEQzk5MERCRDEwRjcpIHdpdGggdGhlIFByb3RlaW5zIGNvbm5lY3RlZCB0byBTdGF0NWIgaW4gdGhpcyBkYXRhc2V0LgoKLSAgIFRoZXJlIGlzIGFsc28gYSBsaW5rIHRvIHRoZSBbU1RSSU5HZGIgd2Vic2l0ZSBmb3IgbW9yZSBzdHJpbmdlbnQgdGhyZXNob2xkID0gMjAwXShodHRwczovL3ZlcnNpb24tMTEtNS5zdHJpbmctZGIub3JnL2NnaS9saW5rP3RvPTE4QjVDM0UxNkU5QjNBRTApIHdpdGggdGhlIFByb3RlaW5zIGNvbm5lY3RlZCB0byBTdGF0NWIgaW4gdGhpcyBkYXRhc2V0LgoKYGBge3J9CiNGaW5kaW5nIFN0cmluZyBpZCBpZGVudGlmaWVyIGZvciBTdGF0NWIgYW5kIGFzc2lnbmluZyBpdCB0byB0aGUgYSB2YXJpYWJsZQppIDwtIGdyZXAoIlA0MjIzMiIsIGhpdHNfbG93JE1hc3Rlci5Qcm90ZWluLkFjY2Vzc2lvbnMpIApTdGF0NWIgPC0gc3RyaW5nX2RiX2xvdyRtcCgiUDQyMjMyIikgI21hcHBpbmcgdGhlIFVOSVBST1QgSWRzIAoKIyMgTWFwcGluZyBTdGF0NWIgaW50ZXJhY3Rpb25zIGluIHRoZSBsZXNzIHN0cmluZ2VudCBkYXRhIHNldCBhbmQgbW9yZSBzdHJpbmdlbnQgZGF0YSBzZXQKCiNEZWZpbmluZyBhIGZ1bmN0aW9uIHRvIGdldCAxLiBQcm90ZWluc19jb25uZWN0ZWRfdG9fU3RhdDViLCAyLiBOZXR3b3JrIHBsb3QsIDMuIExpbmsgdG8gd2VicGFnZSwgNC4gUGF0aHdheXMKc3VibmV0d29ya3MgPC0gZnVuY3Rpb24oZGF0YV9oLHNjb3JlX2gpIHsKICAjYSA9IFRoZSBkYXRhIHdlIHdhbnQgdG8gaGF2ZSBhIG5ldHdvcmsgZnJvbSwgYiA9IHRoZSB0aHJlc2hvbGQgbnVtYmVyIG9mIHRoZSBzdHJpbmdfZGIgZnVuY3Rpb24KICBzdHJpbmdfZGIgPC0gU1RSSU5HZGIkbmV3KHZlcnNpb249IjExLjUiLCBzcGVjaWVzPTEwMDkwLCBzY29yZV90aHJlc2hvbGQgPSBzY29yZV9oLCBpbnB1dF9kaXJlY3Rvcnk9IiIpICNzcGVjaWVzID0gbW91c2UgCiAgZ2V0IDwtIHN0cmluZ19kYiRnZXRfc3VibmV0d29yayhkYXRhX2gpICNidWlsZHMgc3VibmV0d29yayBmcm9tIGV2ZXJ5IGVudHJ5IGluIFNUUklOR19pZAogIGdoaSA8LSBnZXRbW1N0YXQ1Yl1dWzFdICMjZ2l2ZXMgdGhlIGxpc3QgZW50cnkgb2YgImdldCIgdGhhdCBsaXN0cyBhbGwgMTggZW50cmllcyB0aGF0IGludGVyYWN0IHdpdGggU3RhdDViCiAgaGcgPC0gdW5pcXVlKGdoaSRgMTAwOTAuRU5TTVVTUDAwMDAwMTAyOTgxYCkgI2dpdmVzIHRoZSB1bmlxdWUgaW50ZXJhY3Rpb24gKDkgb3V0IG9mIDE4KSwgCiAgbWF0IDwtIGFzX2lkcyhnaGkkYDEwMDkwLkVOU01VU1AwMDAwMDEwMjk4MWApICNmdW5jdGlvbiBhc19pZHMgZnJvbSB0aGUgaWdyYXBoIHBhY2thZ2UgcmVhZHMgdGhlIGNsYXNzKCJpZ3JhcGgudnMiKSAgIGFuZCBjb2xsYXBzZXMgaXQgaW50byBhIG1hdHJpeAogIFByb3RlaW5zX2Nvbm5lY3RlZF90b19TdGF0NWIgPC0gYXMuZGF0YS5mcmFtZSh1bmlxdWUobWF0KSkKICBuYW1lcyhQcm90ZWluc19jb25uZWN0ZWRfdG9fU3RhdDViKVsxXSA8LSAiU1RSSU5HX2lkIiAjUmVuYW1pbmcgY29sdW1uIHNvIHRoYXQgYWRkX3Byb3RlaW5fZGVzY3JpcHRpb24gcmVjb2duaXplcyBpdAogIFByb3RlaW5zX2Nvbm5lY3RlZF90b19TdGF0NWJbKG5yb3coUHJvdGVpbnNfY29ubmVjdGVkX3RvX1N0YXQ1YikrMSksXSA8LSBTdGF0NWIgI0FkZHMgU3RhdDViIHRvIG1hcCBpdHMgY29ubmVjdGlvbnMKICBQcm90ZWluc19jb25uZWN0ZWRfdG9fU3RhdDViIDwtIHN0cmluZ19kYiRhZGRfcHJvdGVpbnNfZGVzY3JpcHRpb24oUHJvdGVpbnNfY29ubmVjdGVkX3RvX1N0YXQ1YikgI0FkZMK0cyBwcm90ZWluIGluZm9ybWF0aW9uIHRvIFNUUklOR2lkwrRzCiAgCiAgI1Bsb3R0aW5nIHRoZSBzdWJuZXR3b3JrCiAgc3RyaW5nX2RiJHBsb3RfbmV0d29yayhQcm90ZWluc19jb25uZWN0ZWRfdG9fU3RhdDViJFNUUklOR19pZCkKICAKICAjcHJvdmlkaW5nIG15IGxpbmsKICBsaW5rIDwtIHN0cmluZ19kYiRnZXRfbGluayhQcm90ZWluc19jb25uZWN0ZWRfdG9fU3RhdDViJFNUUklOR19pZCkgI1Byb3ZpZGVzIGxpbmsgdG8gdGhlIFNUUklOR2RiIHBhZ2UKICAKICAjUHJvdmlkaW5nIHRoZSBwYXRod2F5cwogIHBhdGh3YXlzIDwtIHN0cmluZ19kYiRnZXRfZW5yaWNobWVudChQcm90ZWluc19jb25uZWN0ZWRfdG9fU3RhdDViLCBjYXRlZ29yeSA9ICJLRUdHIikKCiAgcmV0dXJuKGxpc3QoUHJvdGVpbnNfY29ubmVjdGVkX3RvX1N0YXQ1YiwgcGF0aHdheXMsIGxpbmspKQogIAogIHJlbW92ZShnZXQsZ2hpLGhnLG1hdCkKICAgfQoKIyMgTGVzcyBzdHJpbmdlbnQgcGFyYW1ldGVyIHRvIGRldGVybWluZSB0aGUgUHJvdGVpbnMgdGhhdCBpbnRlcmFjdCB3aXRoIFN0YXQ1CnN1Ym5ldHdvcmtfbGVzc19zdHJpbmdlbnQgPC0gc3VibmV0d29ya3MoZGF0YV9oID0gaGl0c19sb3ckU1RSSU5HX2lkLCBzY29yZV9oID0gMSkgI1VzaW5nIGZ1bmN0aW9uCiNTdWItc2V0dGluZyB0aGUgcmV0dXJuZWQgbGlzdCBpbnRvIG9yaWdpbmFsIGRhdGFmcmFtZXMgKyBsaW5rClByb3RlaW5zX2Nvbm5lY3RlZF90b19TdGF0NWJfbGVzcyA8LSBzdWJuZXR3b3JrX2xlc3Nfc3RyaW5nZW50W1sxXV0KcGF0aHdheXNfbGVzcyA8LSBzdWJuZXR3b3JrX2xlc3Nfc3RyaW5nZW50W1syXV0KTGlua19sZXNzIDwtIHN1Ym5ldHdvcmtfbGVzc19zdHJpbmdlbnRbWzNdXQpMaW5rX2xlc3MgI0xpbmsgdG8gd2Vic2l0ZQppbnZpc2libGUoc3RyaW5nX2RiX2xvdyRnZXRfcG5nKFByb3RlaW5zX2Nvbm5lY3RlZF90b19TdGF0NWJfbGVzcyRTVFJJTkdfaWQsIGZpbGUgPSAifi9waG9zcGhvcHJvdGVvbWljc19wcm9qZWN0L3Jlc3VsdHMvTmV0d29ya19TdGF0NV9sZXNzLnBuZyIpKSAjU2F2aW5nIHBuZwoKIyMgTW9yZSBzdHJpbmdlbnQgcGFyYW1ldGVyIHRvIGRldGVybWluZSB0aGUgUHJvdGVpbnMgdGhhdCBpbnRlcmFjdCB3aXRoIFN0YXQ1CnN1Ym5ldHdvcmtfbW9yZV9zdHJpbmdlbnQgPC0gc3VibmV0d29ya3MoZGF0YV9oID0gaGl0c19oaWdoJFNUUklOR19pZCwgc2NvcmVfaCA9IDIwMCkgI1VzaW5nIGZ1bmN0aW9uCiNTdWItc2V0dGluZyB0aGUgcmV0dXJuZWQgbGlzdCBpbnRvIG9yaWdpbmFsIGRhdGFmcmFtZXMgKyBsaW5rClByb3RlaW5zX2Nvbm5lY3RlZF90b19TdGF0NWJfbW9yZSA8LSBzdWJuZXR3b3JrX21vcmVfc3RyaW5nZW50W1sxXV0gCnBhdGh3YXlzX21vcmUgPC0gc3VibmV0d29ya19tb3JlX3N0cmluZ2VudFtbMl1dCkxpbmtfbW9yZSA8LSBzdWJuZXR3b3JrX21vcmVfc3RyaW5nZW50W1szXV0KTGlua19tb3JlICNMaW5rIHRvIHdlYnNpdGUgCmludmlzaWJsZShzdHJpbmdfZGJfaGlnaCRnZXRfcG5nKFByb3RlaW5zX2Nvbm5lY3RlZF90b19TdGF0NWJfbW9yZSRTVFJJTkdfaWQsIGZpbGUgPSAifi9waG9zcGhvcHJvdGVvbWljc19wcm9qZWN0L3Jlc3VsdHMvTmV0d29ya19TdGF0NV9tb3JlLnBuZyIpKSAjU2F2aW5nIHBuZwoKCmBgYAoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIyMjIFByaW50aW5nIFBhdGh3YXlzCgotcHJpbnRpbmcgdGhlIGVucmljaGVkIHBhdGh3YXlzIGZyb20gdGhlIHN1Ym5ldHdvcmtzIG9mIFN0YXQ1YiBhbmQgcmVuYW1pbmcgdGhlbSBmb3IgYSBiZXR0ZXIgaHVtYW4gcmVhZGFiaWxpdHkgJiBmb3JtYXR0aW5nIG9mIGh0bWwgZG9jdW1lbnQuCgpgYGB7ciBwYWdlZC5wcmludD1UUlVFfQojSWRlbnRpZnlpbmcgb25seSBmb3IgUHJvdGVpbnMgY29ubmVjdGVkIHRvIFN0YXQ1YgoKI0Z1bmN0aW9uIHRvIGdlbmVyYXRlIHRhYmVscyBiYXNlZCBvbiBmYWN0b3IgZ2VuZSBjb3VudHMgLyBudW1iZXIgb2YgYW5ub3RhdGVkIGdlbmVzLgpmYWN0b3JfZnVuY3Rpb24gPC0gZnVuY3Rpb24oUGF0aHdheSl7CmkgPSAxCmZvciAoaSBpbiBpOm5yb3coUGF0aHdheSkpIHsKICBQYXRod2F5JGZhY3RvcltpXSA8LSByb3VuZCgoUGF0aHdheVtpLDNdL1BhdGh3YXlbaSw0XSksIGRpZ2l0cyA9IDMpICNyb3VuZGVkIGZvciBiZXR0ZXIgdmlzdWFsaXphdGlvbgp9ClBhdGh3YXkgPC0gcmVuYW1lKFBhdGh3YXksIGJhY2tncm91bmQgPSBudW1iZXJfb2ZfZ2VuZXNfaW5fYmFja2dyb3VuZCkKUGF0aHdheSA8LSByZW5hbWUoUGF0aHdheSwgaGl0cyA9IG51bWJlcl9vZl9nZW5lcykKUGF0aHdheSA8LSByZW5hbWUoUGF0aHdheSwgaW52b2x2ZWRfZ2VuZXMgPSBwcmVmZXJyZWROYW1lcykKUGF0aHdheSA8LSBQYXRod2F5W29yZGVyKFBhdGh3YXkkZmFjdG9yLGRlY3JlYXNpbmcgPSBUICksXQpyZXR1cm4ocHJpbnQuZGF0YS5mcmFtZShQYXRod2F5WzE6NSxjKDExLDMsNCwxMCw3KV0sIHJvdy5uYW1lcyA9IEYpKQp9CiNUYWJsZSBmb3IgbGVzcyBzdHJpbmdlbnQgcGFyYW1ldGVycwpmYWN0b3JfZnVuY3Rpb24ocGF0aHdheXNfbGVzcykKI1RhYmxlIGZvciBtb3JlIHN0cmluZ2VudCBwYXJhbWV0ZXJzCmZhY3Rvcl9mdW5jdGlvbihwYXRod2F5c19tb3JlKQojSWRlbnRpZnlpbmcgcGF0aHdheXMgYWxzbyBmb3IgUHJvdGVpbnMgd2l0aCBhIHJhdGlvID49MiBhbmQgcC52YWx1ZSA8PSAwLjA1IGFuZCB0aHJlc2hvbGQgPSAyMDAuCkJpZ19uZXR3b3JrX3BhdGh3YXlzX21vcmUgPC0gc3RyaW5nX2RiX2hpZ2gkZ2V0X2VucmljaG1lbnQoaGl0c19oaWdoJFNUUklOR19pZCwgY2F0ZWdvcnkgPSAiS0VHRyIpCmZhY3Rvcl9mdW5jdGlvbihCaWdfbmV0d29ya19wYXRod2F5c19tb3JlKQojSWRlbnRpZnlpbmcgcGF0aHdheXMgYWxzbyBmb3IgUHJvdGVpbnMgd2l0aCBhIHJhdGlvID49MiBhbmQgcC52YWx1ZSA8PSAwLjA1IGFuZCB0aHJlc2hvbGQgPSAxLgpCaWdfbmV0d29ya19wYXRod2F5c19sZXNzIDwtIHN0cmluZ19kYl9sb3ckZ2V0X2VucmljaG1lbnQoaGl0c19sb3ckU1RSSU5HX2lkLCBjYXRlZ29yeSA9ICJLRUdHIikKZmFjdG9yX2Z1bmN0aW9uKEJpZ19uZXR3b3JrX3BhdGh3YXlzX2xlc3MpCgpyZW1vdmUoaSwgTGlua19sZXNzLCBMaW5rX21vcmUsIHN0cmluZ19kYl9oaWdoLCBzdHJpbmdfZGJfbG93LCBzdWJuZXR3b3Jrcywgdm9sY2Fub19wbG90X3Byb3Rlb21lLCBzdWJuZXR3b3JrX2xlc3Nfc3RyaW5nZW50LCBzdWJuZXR3b3JrX21vcmVfc3RyaW5nZW50LCBoaXRzX2hpZ2gsIGhpdHNfbG93LCBkYXRhX3Bob3NwaG8sIGZhY3Rvcl9mdW5jdGlvbiwgdm9sY2Fub19waG9zcGhvX3Bsb3QsIHN0cmluZ19kYl92b2xjYW5vKQoKYGBgCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMjIyMgUmVzdWx0cyBhbmQgRGlzY3Vzc2lvbgoKLSAgIFN0YXQ1YiB3YXMgcGlja2VkIGFzIHRoZSBsZWFkIHRhcmdldCBzaW5jZSBpdCBzaG93ZWQgdGhlIGhpZ2hlc3Qgc2lnbmlmaWNhbnQgcmF0aW8gYWZ0ZXIgZGF0YSBjbGVhbiB1cCBpbiB0aGUgcGhvc3BobyBkYXRhIHNldC4KCi0gICBUaGUgbW9yZSBzdHJpbmdlbnQgcGFyYW1ldGVycyB0dXJuZWQgb3V0IGxlc3MgcHJvdGVpbnMgY29ubmVjdGVkIHRvIFN0YXQ1Yi81YSBidXQgd2l0aCBhIGhpZ2hlciBzZWN1cml0eSBvZiB0cnVlIGFzc29jaWF0aW9uLiBUaGUgZ2VuZXJhdGVkIHN1Ym5ldHdvcmsgZm9yIFN0YXQ1YiBzaG93ZWQgYSB2ZXJ5IHByb21pc2luZyB0YXJnZXQgZm9yIGZ1cnRoZXIgaW52ZXN0aWdhdGlvbiwgc2luY2UgYSByZWFsIG5ldHdvcmsgZXZvbHZlcyBhcm91bmQgU3RhdDViLiBHZW5lcmF0ZWQgcGF0aHdheXMgdG8gaW52ZXN0aWdhdGUgdGhlIGVmZmVjdCBvZiBpbmNyZWFzZWQgcGhvc3Bob3J5bGF0aW9uIHJldHVybmVkIG1hbnkgY2FuY2VyLXJlbGF0ZWQgcGF0aHdheXMgaW5kaWNhdGluZyBhIGNvbm5lY3Rpb24gYmV0d2VlbiByZXRhaW5pbmcgYSBub24tZGlmZmVyZW50aWF0ZWQgc3RhdGUgaW4gc3RlbSBjZWxscyBhbmQgdGhlIGNhcGFiaWxpdHkgb2YgY2FuY2Vyb3VzIGNlbGxzIHRvIGZvcm0gY2FuY2Vyb3VzIG1hc3Nlcy4gT2YgdGhlIG5vbi1jYW5jZXIgcGF0aHdheXMgSkFLLVNUQVQgaXMgb2YgbW9zdCBpbXBvcnRhbmNlIHNpbmNlIGl0J3MgYWxzbyB0aGUgc3ViIHBhdGh3YXkgYWZmZWN0ZWQgaW4gdGhlIGNhbmNlciBwYXRod2F5cy4KCi0gICBUaGUgbGVzcyBzdHJpbmdlbnQgcGFyYW1ldGVycyB0dXJuZWQgb3V0IG1vcmUgcHJvdGVpbnMgY29ubmVjdGVkIHRvIFN0YXQ1Yi81YSBidXQgaGFzIGEgaGlnaGVyIHJpc2sgb2YgZmFsc2UgcG9zaXRpdmUgY29ubmVjdGlvbnMuIEluIHRoZSBmb2xsb3cgdXAgcGF0aHdheSBhbmFseXNpcyB0aGlzIHR1cm5lZCBvdXQgdG8gYmUgaXJyZWxldmFudCwgc2luY2UgbWFqb3IgY29udHJpYnV0aW9ucyB0byBwYXRod2F5cyBhcmUgZG9uZSBieSBTdGF0NWIvNWEgdGhhdCBhcmUgcGFydCBvZiBib3RoIGRhdGEgc2V0cy4gVGhpcyBjb3VsZCBiZSBzZWVuIGluIHRoZSBpbnZvbHZlZF9nZW5lcyBzZWN0aW9uIGluIHRoZSBkYXRhIHRhYmxlcy4gU3RhdDViIGZvcm1zIGhvbW8tIGFuZCBoZXRlcm8gZGltZXJzIGVzcGVjaWFsbHkgd2l0aCB0aGUgcGFyYWxvZyBTdGF0NWEuIFVwb24gVHlyLXBob3NwaG9yeWxhdGlvbiB0aGV5IGRpbWVyaXplIGFuZCB0cmFuc2xvY2F0ZSBpbnRvIHRoZSBudWNsZXVzIHRha2VzIHBsYWNlIHdoZXJlIGl0IGFjdHMgYXMgW3RyYW5zY3JpcHRpb24gZmFjdG9yXShodHRwczovL2RvaS5vcmcvMTAuMTA3NC9qYmMuMjc0LjMyLjIyNDg0KS4gVGhlIG1vcmUgc3RyaW5nZW50IHRocmVzaG9sZCBwYXJhbWV0ZXIgYWxzbyByZW1vdmVkIFNvczEgZnJvbSB0aGUgZGF0YXNldC4gU29zMSBpcyBpbnZvbHZlZCB0b2dldGhlciB3aXRoIFN0YXQ1YS9iIGluIG1vc3Qgb2YgdGhlIGVucmljaGVkIHBhdGh3YXlzIGluIHRoZSBsZXNzIHN0cmluZ2VudCBkYXRhc2V0LiBUaGUgUmFzLVJhZl9NQVBLIHBhdGh3YXkgaXMgcmVndWxhdGVkIGJ5IFNvczEsIG5vcm1hbGx5IGFjdGluZyBhcyBndWFuaW4gbnVjbGVvdGlkZSBleGNoYW5nZSBmYWN0b3IgW3Vwb24gUmFzXShodHRwczovL2RvaS5vcmcvMTAuMTA4My9qY2IuMjAwMTAzMTQ2KS4gUGhvc3Bob3J5bGF0aW9uIG9mIHNwZWNpZmljIFNlcmluIHJlc2lkdWVzIGhhdmUgYSBkZWFjdGl2YXRpbmcgZWZmZWN0IG9uIFNvczEgYW5kIGlzIHVzZWQgYXMgbmVnYXRpdmUgcmVndWxhdG9yIG9mIHRoZSBbd2hvbGUgUmFzLVJhZi1NQVBLIHBhdGh3YXldKGh0dHBzOi8vZG9pLm9yZy8xMC4xMDQyL0JKMjAxMjA5MzgpLCBhIHBhdGh3YXkgbm9ybWFsbHkgYXNzb2NpYXRlZCB3aXRoIFtwcm9tb3RpbmcgZGlmZmVyZW50aWF0aW9uXShodHRwczovL2RvaS5vcmcvMTAuMTAwMi9qY3AuMjgzMzQpLiBTb3MxIGlzIGFsc28gaW52b3ZsZWQgaW4gdGhlIG1UT1IgcGF0aHdheSwgdG9nZXRoZXIgd2l0aCBBa3QxczEgKHN1YnVuaXQgb2YgbVRPUkMxKSwgYm90aCBhbHNvIGZvdW5kIGluIHRoZSBsZXNzIHN0cmluZ2VudCBzdWJuZXR3b3JrIGJ1dCBub3QgdGhlIG1vcmUgc3RyaW5nZW50IG9uZS4KCiAgICAhW21UT1ItcGF0aHdheSB3aXRoIEFrdDFzMSAoUFJBUzQwKSBhbmQgU29zMSBtYXJrZWQgaW4gcGluayBmcm9tIEtFR0csIGVudHJ5IFttbXUwNDE1MF0oaHR0cHM6Ly93d3cua2VnZy5qcC9wYXRod2F5L21tdTA0MTUwKS5dKGltYWdlcy9tVE9SX3BhdGh3YXkucG5nKQoKLSAgIFVuZGVyIHRoZXNlIGJpb2xvZ2ljYWwgY2lyY3Vtc3RhbmNlcyB0aGVzZSBmb3VuZHMgY2FuIGJlIGNvdW50ZWQgYXMgdmFsaWQgYW5kIGFyZSBvYmplY3QgZm9yIGZ1cnRoZXIgaW52ZXN0aWdhdGlvbiwgd2hpbGUgb3RoZXIgaGl0cyBpbiB0aGUgbGVzcyBzdHJpbmdlbnQgZGF0YSBzZXQgbWF5IGJlIGZvdW5kIGFzIGZhbHNlIHBvc2l0aXZlcy4gVGhpcyB3YXMgYSB2ZXJ5IGdvb2QgZXhhbXBsZSBvZiB0aGUgdHJhZGUtb2YgZW5jb3VudGVyZWQgYnkgdGhyZXNob2xkIHNldHRpbmdzIHRoYXQgbXVzdCBiZSBjb250cm9sbGVkIHRob3JvdWdoIHRoZSBkYXRhIGFuYWx5c2lzLgoKIyMjIyBDb25jbHVzaW9uCgotICAgTWFueSBjYW5jZXJzIGludm9sdmVkIHBhdGh3YXlzIGFyZSBhbHNvIGFjdGl2YXRlZCB0byBlbnN1cmUgc3RlbW5lc3MgaW4gbm9uLWRpZmZlcmVudGlhdGVkIG11c2NsZSBtb3VzZSBjZWxscyB1cG9uIGRpZmZlcmVudGlhdGlvbiBzdGltdWxpLgotICAgTWFqb3IgdGFyZ2V0cyBmb3IgZnVydGhlciBpbnZlc3RpZ2F0aW9ucyBhcmUgSkFLLVNUQVQgcGF0aHdheSBwcm90ZWlucyBhbmQgU29zMSBhbmQgQWt0MXMxIHdpdGggdGhlIG1UT1ItIGFuZCBNQVBLLXBhdGh3YXkuIEVzcGVjaWFsbHkgaWRlbnRpZmljYXRpb24gb2YgcGhvc3Bob3J5bGF0aW9uIHNpdGVzIGluIHRoZSBwcm90ZWluIGFuZCBleHBlcmltZW50cyB3aXRoIGRpZmZlcmVudCBkaWZmZXJlbnRpYXRpb24gaW5oaWJpdG9ycyB0byBlbnN1cmUgdW5zcGVjaWZpYyBhY3RpdmF0aW9uIG9mIHN0ZW1uZXNzIHByb3RlY3RpbmcgcGF0aHdheXMgb3IgaWRlbnRpZmljYXRpb24gb2YgYWx0ZXJuYXRpdmVseSBhY3RpdmF0ZWQgcGF0aHdheXMgY291bGQgcHJvdmlkZSBtb3JlIGluc2lnaHRzLgotICAgQWxzbywgcG9zc2libHkgYSByZXByb2R1Y3Rpb24gb2YgdGhlIGRhdGFzZXQgd2l0aCBtb3JlIHNhbXBsZXMgdG8gaWRlbnRpZnkgd2l0aCBhIGhpZ2hlciBzZWN1cml0eSB0aGUgZWxldmF0aW9uIG9mIGludm9sdmVkIHByb3RlaW5zIGFuZCBhbHNvIHJlcHJvZHVjdGlvbiBpbiBkaWZmZXJlbnQgY2VsbCBsaW5lcyB0byBlbnN1cmUgdGhhdCB0aGUgb2JzZXJ2ZWQgZWZmZWN0cyBhcmUgc3RlbW5lc3MgcmVsYXRlZCBhbmQgbm90IGNlbGwgbGluZSByZWxhdGVkIHdvdWxkIGJlIGludGVyZXN0aW5nIHJlc2VhcmNoIHRhcmdldHMuIFNpbmNlIHRoZSBiaW9pbmZvcm1hdGljIHBpcGVsaW5lIGlzIGluIHBsYWNlLCB0aGUgYW5hbHlzaXMgY291bGQgYmUgY29uZHVjdGVkIHF1aWNrbHkuCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQo=